package io.github.wanggit.antrpc;

import io.github.wanggit.antrpc.commons.config.Configuration;
import io.github.wanggit.antrpc.commons.test.WaitUtil;
import io.github.wanggit.antrpc.commons.utils.NetUtil;
import io.vavr.CheckedFunction1;
import io.vavr.Tuple2;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.mock.env.MockEnvironment;

public abstract class ServerUtil {

    public static Tuple2<AntrpcContext, GenericApplicationContext> createRpcServer(
            String serverName, ConfigurationCallback configurationCallback, Class... classes)
            throws Throwable {
        return createRpcServer(serverName, configurationCallback, null, classes);
    }

    public static Tuple2<AntrpcContext, GenericApplicationContext> createRpcServer(
            String serverName,
            ConfigurationCallback configurationCallback,
            CheckedFunction1<GenericApplicationContext, Void> afterRefreshFunction,
            Class... classes)
            throws Throwable {
        // server
        GenericApplicationContext genericApplicationContext = new GenericApplicationContext();
        MockEnvironment environment = new MockEnvironment();
        int rpcPort = NetUtil.randomVisiblePort();
        environment
                .withProperty("server.port", String.valueOf(NetUtil.randomVisiblePort()))
                .withProperty("antrpc.port", String.valueOf(rpcPort))
                .withProperty(
                        "spring.application.name",
                        serverName + RandomStringUtils.randomAlphanumeric(30));
        genericApplicationContext.setEnvironment(environment);
        for (int i = 0; i < classes.length; i++) {
            Class clazz = classes[i];
            GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
            genericBeanDefinition.setBeanClass(clazz);
            genericApplicationContext.registerBeanDefinition(
                    clazz.getName(), genericBeanDefinition);
        }

        genericApplicationContext.refresh();
        if (null != afterRefreshFunction) {
            afterRefreshFunction.apply(genericApplicationContext);
        }

        AntrpcContext antrpcContext = new AntrpcContext(new Configuration());
        configurationCallback.initConfiguration((Configuration) antrpcContext.getConfiguration());

        Configuration configuration = (Configuration) antrpcContext.getConfiguration();
        configuration.setPort(rpcPort);
        configuration.setStartServer(true);
        configuration.setEnvironment(environment);
        antrpcContext.init(genericApplicationContext);
        WaitUtil.wait(3, 1);
        return new Tuple2<>(antrpcContext, genericApplicationContext);
    }

    public interface ConfigurationCallback {
        default void initConfiguration(Configuration configuration) {}
    }
}
